iT邦幫忙

2026 iThome 鐵人賽

DAY 12
0
Claude AI

Claude × Playwright:30 天打造你的 Agentic SDET 同事系列 第 12

Day 12|Pass 和 Fail 之外的多重宇宙:設計結構化測試結果

  • 分享至 

  • xImage
  •  

前言

昨天新同事提供了完整的證據,我們也看完這個證據裡面的內容,並且閱讀了新同事觀察到的現象與結論,而每個結論其實都包含著證據。我們閱讀了這份新同事的筆記,但其實我們今天無法知道哪些證據、或是哪些新同事做了哪些事情,哪些是符合我們預期的,而哪些又不是。
所以我們今天就要根據新同事提供的這個 notes.md,去看看是不是還有東西是我們要處理的。

先看看 notes.md 裡面寫了哪些

...

畫面小計顯示 0

購物車列:Combination Pliers / 單價 $14.15 / 小計 $00.00,而表格底部總計是 $14.15。資料層的 total 明明是 14.15。

標題欄位重複

購物車表頭為 Item | Quantity | Price | Total | Total,最後兩欄同名。

使用者選單顯示查無使用者

登入後導覽列顯示「User Data not found」而不是姓名,但同一頁的 GET /users/me 回 200 OK。

一致性判斷

購物車頁:該列小計 $00.00,表尾總計 $14.15

https://ithelp.ithome.com.tw/upload/images/20260826/20169442VLj89Nr8Kx.png

這個問題是說,在同一張表格裡面,我們的小計是 0.0 元,但是我們在表尾顯示的其實是 14.15 元。
如果我們今天搭配一些折價券,有可能導致它的總金額是 0 元,但不可能導致它最後結算的時候是 14.15 元。所以其實這很明顯是一個 Bug。
像這種不需要額外產品規格說明的知識,其實就是一種一致性的判斷。

為什麼 Pass 和 Fail 兩格不夠用

通常你會發現,測試案例的通過標準只有一個,就是它必須要符合你預期的行為。但當一個測試案例失敗的時候,失敗的原因可能會有很多:

  1. 不符合預期:甚至它可能是一個潛在的缺陷。
  2. 前置條件設置失敗:在設置一些前置條件時就失敗了,這也會被算作是一個失敗的 case。
  3. 測試結果不穩定:有些測試案例有時候會通過,有時候會失敗,這時候它也可以算是一個失敗的測試案例。
  4. 測試到了非預期範圍:我們可能測試到了這個測試案例原本沒有要測試的地方,但它卻導致了測試案例失敗。
  5. 缺乏證據:我們測到了一些問題,但沒有證據顯示它是一個缺陷。

因此,我們可以把失敗的測試案例分為下列幾類:

status 分析
pass 表示測試案例皆如預期的行為發生,甚至包含錯誤的情境,也會被正確擋下。
fail 表示行為不符合我們預期,它有可能是潛在的一個 bug。
blocked 前置條件可能沒有順利的設置成功,甚至沒有測到我們要測試的地方。
flaky 有時候測試案例通過,有時候失敗,它會隨機的發生。
anomaly 可能包含在這個測試案例要測試的範圍之內,它可能是我們還無法判定的可疑現象。
inconclusive 我們測到了一些異常,但是證據不足以判斷它是一個缺陷。

在這些類型之中,有幾種組合是比較難判斷的,例如 Block 和 Inconclusive。我舉一個例子:如果今天我們去一個購物網站,我們其實讀到產品的資訊,但畫面上整個產品的資訊整塊是沒有顯示的。同時間,我們在 API(也就是 Network 那邊)可能有看到 GET 傳回 200,證實整個畫面是有被渲染的,這樣的情況它可能就是 Inconclusive 的意思是不確定。它可能是因為網路或是某些環境造成的異常,導致我們沒辦法確定它是一個 block 的行為。

但如果今天我們在瀏覽網站的時候,看到商品頁面整個是無法撈進來的,甚至給了其他的 error(例如說 read error 403 或 404),這時候這樣的錯誤其實它就是一個 Block 的行為。

另外一個比較難判斷的,應該是 Fail 和 Anomaly。

如果今天在同一個購物車裡面的數量,它可能有兩種情況:

  1. 輸入負數(例如輸入 -5):

前端可能沒有驗證,所以導致總共的 Total 金額顯示的是負 70.75 元。這時候我們就有明確的預期行為,其實我們應該要擋下負數,所以這時候這個測試案例的失敗原因,其實它就是個 Fail。

  1. 輸入成 0:

結果它的 Total 變成 0,但你會發現到結帳的時候,它的這個金額是沒有重新被算過。那這時候我們就是要看規格上面,是不是每次在結帳之後,金額都要重算?即使它是 0。所以這樣的情況就是 Anomaly。

當我們遇到 Anomaly 的時候,其實我們可以先把它記錄下來,並不用當下就判斷它是一個缺陷,或者它是一個 Fail。

最難判斷的一個就是 flaky(不穩定的測試),要看它是穩定壞掉的,還是隨機壞掉的。

通常我們在 rerun 的時候:

  1. 如果它是穩定壞掉,而且我們可以不斷地重現這個問題,那這時候其實我們就可以判斷它是一個 fail,不是 flaky
  2. 如果我們跑了四次,兩次失敗、兩次成功,那這時候我們就要把這次的失敗原因視為是一個 flaky。

通常我們在確定 flaky 的 detection 時,可能要多跑幾次,才可以知道它的 pass rate 跟 fail rate。如果它是一個很隨機的、或是 50/50 的機率,那它肯定就是一個 flaky;但如果它基本上每次都是 fail 的話,那它基本上就是一個 fail 的 test case。

最後一種是我們常常會發現的,就是整個 UI 的流程都是 pass,但其實在 console 裡會有一些紅字。

有時候紅字可能跟測試的行為沒有關係,這時候如果行為是符合的,我們可能不會管畫面上的紅字,正常來說它就是 pass。

但是,如果我們發現這個 error 可能是跟行為有關的,也許我們就要判斷它是一個 anomaly,並且告知理由是什麼。我們並不能因為畫面沒有問題,但 console 有一些 error,而忽略了一些潛在的問題。

實驗:同一包證據,12 個檢查點分出四種結果

/sdet-skills:structured-result 讀 output/evidence/20260806-with-bugs-add-to-cart/,把這一輪的檢查點寫成 results.yaml

所以今天我們要透過新的 skill structured-result,去讀我們在昨天產生的那一包證據,並且把這些測試的證據寫成 results.yaml

這個 YAML 檔案是可以讓其他 skill 讀取的,我們會在之後的天數,再教這個系統是如何使用這個 results.yaml

在這個封包裡面其實有兩個檔案:

  1. notes.md:會是系統對於在探索這個網站或尋找 bug 時,所觀察到的現象與結論。
  2. manifest.md:告訴你這些產生的資料包裡面有哪些東西、放在哪裡。

而我們今天寫的這個 results.yaml,主要是要給其他 skill 使用,讓後續的系統可以順利完成下一個動作;或者如果有其他需要閱讀這一次跑出來的結果,也可以去閱讀這個 results.yaml

使用這個 skill 會產生一個 results.yaml 的 YAML 檔案。今天我們就來看看這個檔案裡面會有哪些內容。

在 content 裡面通常會列出:

  1. 這個檢查點的 id
  2. 它的 status 是什麼
  3. 他預期的行為是什麼
  4. 他真正觀察到的行為
  5. 這些行為當下的一些證據是在哪裡
results:
  - id: R-01
    check: "以 customer@practicesoftwaretesting.com / welcome01 登入"
    status: pass
    expected: "帳密不被接受時應擋下並顯示錯誤訊息"
    actual: "POST /users/login 回 401,畫面顯示「Invalid email or password」,擋下的行為與 API 一致。同一組帳密在 clean 站回 200,故本站是種子資料不同(test-data),非產品錯"
    evidence: [03-login-after.png, notes.md, "../20260806-clean-add-to-cart/notes.md"]

  - id: R-04
    check: "登入後導覽列應顯示使用者名稱"
    status: fail
    expected: "顯示該帳號的姓名(對照組同一位置顯示 Jane Doe)"
    actual: "顯示「User Data not found」,但同一頁 GET /users/me 回 200 OK。API 拿得到資料、畫面宣告查無使用者"
    evidence: [08-account-after.png, "network.log#L5", "../20260806-clean-add-to-cart/03-account-after.png"]

  - id: R-11
    check: "註冊頁國家下拉選取後表單應成為 valid"
    status: inconclusive
    reason: "以 select_option 選取後 DOM value 已是 NL,Angular 控制項仍為 ng-pristine ng-invalid,送出被擋在「Country is required.」;手動 dispatch change/input 事件才轉 ng-valid。無法分離是產品的事件繫結問題,還是這支 CLI 的 select 實作差異——沒有以真人手動操作重跑一次的證據"
    evidence: [notes.md, 04-register-before.png]

  - id: R-12
    check: "頁面對外的第三方請求"
    status: anomaly
    detail: "cdn-cgi/rum 的 beacon 多次 net::ERR_ABORTED(另有兩次回 204);屬 Cloudflare RUM 遙測,不在本輪受測範圍,只記錄不判定"
    evidence: ["network.log#L3", "network.log#L4", "network.log#L13", "network.log#L14"]

第一個是登入被拒的案例。

這組帳密不是亂打的 —— 它在 clean 站登得進去(POST /users/login200),到了 with-bugs 站卻回 401,畫面顯示「Invalid email or password」。

登入頁顯示 Invalid email or password
https://ithelp.ithome.com.tw/upload/images/20260826/20169442y7GRoOCg1Y.png

03-login-after.png:帳密被擋下,訊息與 401 一致。

但這一筆是 pass。因為產品該做的事都做對了:帳密在這個站不存在,它擋下來、訊息與 API 狀態碼一致。真正的原因是兩個站的種子資料不同,那是測資問題,不是缺陷。

這就是散文看不出來的地方。notes.md 裡它是一條「登入失敗」的觀察,讀起來像出事;填進 results.yaml 才顯示出它其實是通過。

第二個案例是一個 fail case:

今天我們在登入這個網站之後,帳號的姓名欄位應該要顯示「使用者名稱」,但今天發現它顯示的是 "User data not found"。

with-bugs 版登入後,導覽列顯示 User Data not found
https://ithelp.ithome.com.tw/upload/images/20260826/20169442CDxJBZvzE3.png

with-bugs 版:登入成功,導覽列說查無使用者。

乾淨版登入後,同一位置顯示 Jane Doe
https://ithelp.ithome.com.tw/upload/images/20260826/201694422ld9jTUBnS.png

乾淨版同一個位置顯示 Jane Doe。expected 那一欄寫得出「對照組顯示什麼」,靠的就是這張。沒有它,你只能寫「應該顯示姓名」,那是你的期待不是產品的承諾。

妙的是,在同一頁的時候,它拿到的 User API 其實是回傳 200 OK 的,這代表我們其實是有拿到資料的。像這種就是一致性的判斷問題,所以很明顯這就是一個 Bug。

第三個 R11 的 Case,我們在下拉國家的時候,它可能會發現它在拉的時候會被擋在「Country is required」。

這時候我們沒辦法去區分它到底是不是產品的問題,還是因為 Playwright CLI 的 selector 和人類去點選的行為不一樣。
有的時候我們必須把問題分離出來,可能真的需要有人去動手測試一次。
像這樣的錯誤,它就會被表示為 inconclusive。

最後一個案例是頁面對第三方發出的遙測請求。

頁面載入時會打 Cloudflare 的 cdn-cgi beacon,其中幾筆被中止(net::ERR_ABORTED),但這並不在我們要測的範圍之內。它只是一個頁面對於第三方的請求,所以這時候我們就會判斷成 anomaly

然而這一輪總共有 12 個檢查點,你可以在 results.yaml 這個檔案裡面看到它們各自的結果。

我們可以統計出來,這一包證據下面其實是有:

  • 4 個 pass
  • 6 個 fail
  • 1 個 inconclusive
  • 1 個 anomaly

這四個數字是散文交不出來的東西。同一包證據、同一份 notes.md,人讀完只會得到「有幾個地方怪怪的」;填成 results.yaml 之後,它變成可以統計、可以跟上一輪比、可以交給下一支 skill 直接接手的輸入。

小結

通過只有一種樣子,失敗有五種原因,把它們全塞進 fail 這一格,你就分不出該修產品、該修測資、該補證據,還是該先重跑幾次。所以結果要寫成六個狀態的結構化檔案,每一筆掛上 expectedactual 與指得到的 evidence。判不動的時候有兩條退路 —— 證據不足寫 inconclusive、不在守備範圍寫 anomaly —— 而不是硬塞進 pass 或 fail。

              一包證據 + notes.md(散文)
                          │
                          ▼
              /sdet-skills:structured-result
                          │
                          ▼
                    results.yaml
              每筆:check|status|expected
                    actual|evidence
                          │
        ┌────────┬────────┼────────┬────────┬────────┐
        ▼        ▼        ▼        ▼        ▼        ▼
      pass     fail    blocked   flaky   anomaly  inconclusive
     如預期   不符預期  前置沒成功  時好時壞  範圍外    證據不足
     (含該擋           沒測到    要多跑    只記錄    要補證據
      的有擋)                    幾次才算            或找人手測
        │        │        │        │        │        │
        └────────┴────────┴───┬────┴────────┴────────┘
                              ▼
                    數得出來、比得了上一輪
                    下一支 skill 直接吃
                              │
                              ▼
              本輪:4 pass / 6 fail / 1 inconclusive / 1 anomaly

通過只有一種,失敗有很多種,混成一格就失去分流的能力,而分流決定了誰該修。pass 不等於沒事,fail 也不等於產品壞了:那筆登入被拒是 pass(產品該擋有擋,是兩站種子資料不同),這件事讀散文看不出來。判不動就寫判不動,inconclusiveanomaly 是兩條正當的退路,硬判成 fail 只會讓下游多花一輪去推翻你。

下一步

今天我們根據在 explore 這些網站時所發現、錄下的證據,甚至將這些發生的行為跟證據連接,去判斷說這個行為是不是正常的。

如果是通過的話,那就代表這個行為符合預期。但如果今天這個行為不符合預期,其實它有大概 5 種不同的失敗原因。

明天我們就要把這些前面說的 results.yaml 去做分類,釐清測試案例失敗時,到底是哪一種原因造成的。


參考資料

  1. James Bach & Jon Bach — Session-Based Test Management - results.yaml 的祖宗:session sheet 規定一輪要交代涵蓋範圍、發現的問題與時間分配。2000 年的做法就已經不是只交 Pass/Fail。差別在讀者 —— session sheet 是寫給經理看的,results.yaml 是寫給下一支 skill 吃的
  2. Playwright Test — Annotations - 框架只給 skip/fixme/fail,六種狀態要塞哪裡
  3. pytest — Skip and xfail - 另一個生態的同一個問題:skip 混著兩種意思
  4. MDN — 423 Locked - R-01 那筆帳號鎖定的狀態碼語意

上一篇
Day 11|每個結論都要有證據:讓證據包能交棒
下一篇
Day 13|看到異常不代表找到 Bug:教同事先分類
系列文
Claude × Playwright:30 天打造你的 Agentic SDET 同事16
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言